﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace DotNetFunctionDemo.CommonLib
{
    public class DemoEmit
    {
        private delegate int HelloDelegate(string msg, int ret);
        public static void HelloWorldEmit()
        {
            Type[] hellorArgs = { typeof(string), typeof(int) };
            //function int hello(string message,int valueToReturn);
            DynamicMethod hello = new DynamicMethod("hello", typeof(int), hellorArgs, typeof(string).Module);

            Type[] writeStringArgs = { typeof(string) };
            //hello body Console.WriteLine(message);
            MethodInfo consoleWriteLine = typeof(Console).GetMethod("WriteLine", writeStringArgs);
            ILGenerator il = hello.GetILGenerator(256);
            //hello body Console.WriteLine(message);
            il.Emit(OpCodes.Ldarg_0);
            il.EmitCall(OpCodes.Call, consoleWriteLine, null);
            //valueToReturn
            il.Emit(OpCodes.Ldarg_1);
            //return ;
            il.Emit(OpCodes.Ret);
            hello.DefineParameter(1, ParameterAttributes.In, "message");
            hello.DefineParameter(2, ParameterAttributes.In, "valueToReturn");
            HelloDelegate helloFunc = (HelloDelegate)hello.CreateDelegate(typeof(HelloDelegate));
            var result = helloFunc("ni hao", 3);

        }

        public static int Add(int a, int b)
        {
            int sum = 0;
            sum = a + b;
            return sum;
        }
        //public Func<int, int, int> AddFunc;
        public static readonly Func<int, int, int> AddEmit = GetAddMethodByEmit();
        public static Func<int, int, int> GetAddMethodByEmit()
        {
            DynamicMethod AddFunc = new DynamicMethod("Add", typeof(int), new Type[] { typeof(int), typeof(int) });
            ILGenerator il = AddFunc.GetILGenerator();
            LocalBuilder locSum = il.DeclareLocal(typeof(int));
            //sum = 0;
            il.Emit(OpCodes.Ldc_I4_0);
            il.Emit(OpCodes.Stloc_0);
            //get a,b;
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Add);
            il.Emit(OpCodes.Stloc_0);

            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Ret);

             var addMethod = (Func<int, int, int>)AddFunc.CreateDelegate(typeof(Func<int, int,int>));
            return addMethod;
        }

        public static T MapTableToObject<T>(DataTable dt) where T:new()
        {
            if (dt == null && dt.Rows.Count <=0)
            {
                return default(T);
            }
            var row = dt.Rows[0];
            T instance = (T)Activator.CreateInstance(typeof(T));
            var props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (DataColumn col in dt.Columns)
            {
                var objProp = (from p in props where string.Compare(p.Name, col.ColumnName, true) == 0 select p).FirstOrDefault();
                if (objProp == null)
                {
                    continue;
                }
                if (objProp.CanWrite)
                {
                    //这里没有考虑类型转化 默认情况下保持一直 table列和class中的能转化。
                    var val = row[objProp.Name].ToString();
                    objProp.SetValue(instance,val);
                }
            }
            return instance;
        }
        public static int LoopSum(int[] numbers)
        {
            int sum = 0;
            for (int i = 0; i < numbers.Length; i++)
            {
                sum = sum + numbers[i];
            }
            return sum;
        }

        public static int EmitLoop(int[] numbers)
        {
            int sum = 0;
            for (int i = 0; i < numbers.Length; i++)
            {
                sum = sum + numbers[i];
            }
            return sum;
        }

        public static object genAssembly<TIn>(TIn stu)
        {
            AssemblyName assName = new AssemblyName("DemoEmit.EmitObject");
            AppDomain domain = AppDomain.CurrentDomain;
            AssemblyBuilder assBuilder = domain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.RunAndSave);
            var fileName = "DemoEmitObject.exe";
            var binPath = Path.Combine(domain.BaseDirectory, "DemoEmitObject");
            ModuleBuilder modBuilder = assBuilder.DefineDynamicModule(fileName, fileName);

            TypeBuilder typeBuilder = modBuilder.DefineType("DemoEmit.EmitObject.StudentOther", TypeAttributes.Public);

            //MethodBuilder methodBuilder = typeBuilder.DefineMethod("Add",MethodAttributes.Public, typeof(int), new Type[] { typeof(int), typeof(int) });
            foreach (PropertyInfo item in typeof(TIn).GetProperties())
            {
                
                FieldBuilder filedBuilder = typeBuilder.DefineField(item.Name,item.PropertyType, FieldAttributes.Public);
                var val = item.GetValue(stu);
                filedBuilder.SetConstant(val);
            }
            foreach (FieldInfo item in typeof(TIn).GetFields())
            {

            }
            var t = typeBuilder.CreateType();
            assBuilder.Save("DemoEmitObject.exe");
            return Activator.CreateInstance(t);
        }
    }
}
